#ifndef CORE_SINGLETON_HPP_
#define CORE_SINGLETON_HPP_

#include <boost/atomic.hpp> //for atomic use
#include <tbb/compat/thread>

#include "at_exit_manager.hpp"

TB_NAMESPACE_BEGIN
    
template <typename ValueType>
struct DefaultSingletonAllocatorTrait {
  static ValueType* New() { return new ValueType(); }
  static void Delete(ValueType* x) { delete x; x = 0; }
};

template <
  typename ValueType,
  typename AllocatorTraits = DefaultSingletonAllocatorTrait<ValueType> >
class Singleton {
public:
  static ValueType *Instance() {
    static ValueType* sBeingCreatedMarker = reinterpret_cast<ValueType *>(1);

    ValueType *val = ins_.load(memory_order_acquire);
    if (val != 0 && val != sBeingCreatedMarker) {
      return val;    
    }

    ValueType* expect = 0;
    if (ins_.compare_exchange_strong(
      expect, sBeingCreatedMarker, memory_order_acquire)) {
      ValueType* ins = AllocatorTraits::New();
      ins_.store(ins, memory_order_release);
      AtExitManager::RegisterAtExit(&Singleton::OnDestroy);
      return ins;
    }

    while (true) {
      val = ins_.load(memory_order_relaxed);
      if (val != sBeingCreatedMarker) {
        break;
      }
      std::this_thread::yield();
    }
    return val;
  }

  static ValueType *Get() {
    return Instance();
  }
  
  ValueType *operator->() {
    return Instance();
  }

  ValueType &operator*() {
    return *Instance();
  }

  static void ForceDestroy() {
    ValueType* val = ins_.exchange(0, memory_order_acquire);
    AllocatorTraits::Delete(val);
  }
private:
  static void OnDestroy() {
    ForceDestroy();
  }
  static atomic<ValueType *> ins_;
};

template <typename ValueType, typename AllocatorTraits>
atomic<ValueType *> Singleton<ValueType, AllocatorTraits>::ins_;

TB_NAMESPACE_END

#define DECLARE_SINGLETON(cls) \
public:\
  friend class TB_NAMESPACE::Singleton<cls>; \
  friend struct TB_NAMESPACE::Singleton<cls>::AllocatorTraits; \
  static cls *Instance() { \
    return TB_NAMESPACE::Singleton<cls>::Instance(); \
  }

#endif //CORE_SINGLETON_HPP_
